﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Styx.WoWInternals.WoWObjects;
using Styx;
using Styx.Logic;
using Styx.Logic.Pathing;
using Styx.WoWInternals;
using Styx.Helpers;

namespace FelMaster
{
    public class GroupBase
    {
        /* putting all the raiding/group logic here */

        private static LocalPlayer Me { get { return StyxWoW.Me; } }

        private static GroupBase instance = new GroupBase();
        public static GroupBase Instance
        {
            get
            {
                return instance;
            }
        }

        private GroupBase()
        {

        }

        public enum GroupType
        {
            SINGLE,
            PARTY,
            RAID
        }

        public enum GroupLogic
        {
            PVE,
            BATTLEGROUND
        }

        public GroupType Group
        {
            get
            {
                if (Me.IsInParty)
                    return GroupBase.GroupType.PARTY;
                else if (Me.IsInRaid)
                    return GroupType.RAID;
                else
                    return GroupBase.GroupType.SINGLE;
            }
        }

        public GroupLogic Logic
        {
            get
            {
                if (Battlegrounds.IsInsideBattleground)
                {
                    return GroupLogic.BATTLEGROUND;
                }
                else
                {
                    //TODO: check if in arena
                    return GroupLogic.PVE;
                }
            }
        }

        public List<WoWUnit> NearbyUnits(WoWPoint fromLocation, double radius, bool playersOnly)
        {
            List<WoWUnit> hostile = ObjectManager.GetObjectsOfType<WoWUnit>(true, false);
            var MaxDistance2 = radius * radius;

            if (playersOnly)
            {
                hostile = hostile.Where(x =>
                    x.IsPlayer &&
                    IsAttackable(x) &&
                    x.Location.Distance2DSqr(fromLocation) < MaxDistance2
                ).ToList();
            }
            else
            {
                hostile = hostile.Where(x =>
                    !x.IsPlayer &&
                    IsAttackable(x) &&
                    x.Location.Distance2DSqr(fromLocation) < MaxDistance2
                ).ToList();
            }

            return hostile;
        }

        //helper to find packed targets to AoE
        /*
         * radius: aoe spell radius
         * min/max distance: return value bounded between these values
         * playersOnly: toggle pvp mode
         */
        public WoWPoint FindPackedTargets(double radius, double minDistance, double maxDistance, int minTargets, bool playersOnly)
        {
            List<WoWUnit> hostile = ObjectManager.GetObjectsOfType<WoWUnit>(true, false);
            List<WoWUnit> avoid = new List<WoWUnit>();
            var maxDistance2 = (maxDistance + radius) * (maxDistance + radius);

            if (playersOnly)
            {
                hostile = hostile.Where(x =>
                    x.IsPlayer &&
                    IsAttackable(x) &&
                    x.Distance2DSqr < maxDistance2
                ).ToList();
            }
            else
            {
                hostile = hostile.Where(x =>
                    !x.IsPlayer &&
                    IsAttackable(x) &&
                    x.Distance2DSqr < maxDistance2
                ).ToList();
                avoid = hostile.Where(x =>
                    //check for controlled units, like sheep etc
                    SimCraftBase.Instance.UnitIsControlled(x, true)
                ).ToList();
            }

            if (hostile.Count < minTargets)
            {
                return WoWPoint.Empty;
            }

            var score = minTargets-1;
            var best = WoWPoint.Empty;

            for (var x = Me.Location.X - maxDistance; x <= Me.Location.X + maxDistance; x++)
            {
                for (var y = Me.Location.Y - maxDistance; y <= Me.Location.Y + maxDistance; y++)
                {
                    var spot = new WoWPoint(x, y, Me.Location.Z);
                    var dSquare = spot.Distance2DSqr(Me.Location);
                    if (dSquare > maxDistance * maxDistance || dSquare < minDistance*minDistance)
                    {
                        continue;
                    }

                    if (avoid.Any(t => t.Location.Distance2DSqr(spot) <= radius*radius))
                    {
                        continue;
                    }

                    var hits = hostile.Count(t => t.Location.DistanceSqr(spot) < radius * radius);
                    if (hits > score)
                    {
                        best = spot;
                        score = hits;
                        SimCraftBase.Instance.Log("FindPackedTargets(range=" + minDistance + "-" + maxDistance + ", radius=" + radius + ") => SCORE=" + score + " at " + spot);
                        foreach (var u in hostile.Where(t => t.Location.DistanceSqr(spot) < radius * radius))
                            SimCraftBase.Instance.Log(" -> " + u.Name + " " + u.Level);
                        SimCraftBase.Instance.Log("---------------------");
                    }
                }
            }

            return best;
        }

        //returns list of most focused mobs by players
        public struct FocusedUnit
        {
            public int playerCount;
            public WoWUnit unit;
        }

        private bool IsAttackable(WoWUnit unit)
        {
            return
                unit.IsValid &&
                unit.IsAlive &&
                unit.CanSelect &&

                //never focus a player pet - mobs pets are fine
                (unit.OwnedByUnit == null || !unit.OwnedByUnit.IsPlayer) &&
                (unit.CreatedByUnit == null || !unit.CreatedByUnit.IsPlayer) &&

                //other stuff
                !unit.IsNonCombatPet &&
                !unit.IsFriendly;
        }

        private List<FocusedUnit> _mostFocusedUnits = null;
        private DateTime _mostFocusedUnits_timer = DateTime.MinValue;
        private void RefreshMostFocusedUnits()
        {
            var hostile = ObjectManager.GetObjectsOfType<WoWUnit>(true, false).
                    Where(x =>
                        IsAttackable(x) &&
                        //check for controlled units, like sheep etc
                        !SimCraftBase.Instance.UnitIsControlled(x, true)
                    );

            if (Group == GroupType.SINGLE)
            {
                hostile = hostile.Where(x => x.Distance2DSqr < 100 * 100).OrderBy(x => x.CurrentHealth);
                var ret = new List<FocusedUnit>();
                foreach (var h in hostile)
                    ret.Add(new FocusedUnit { unit = h });
                _mostFocusedUnits = ret;
            }
            else //raid or party
            {
                var friends = Me.IsInRaid ? Me.RaidMembers : Me.PartyMembers;
                var ret = new List<FocusedUnit>();
                foreach (var h in hostile)
                    ret.Add(new FocusedUnit { unit = h, playerCount = friends.Count(x => x.CurrentTargetGuid == h.Guid) });
                _mostFocusedUnits = ret.OrderByDescending(x => x.playerCount).ToList();
            }
        }

        public List<FocusedUnit> MostFocusedUnits
        {
            get
            {
                if (DateTime.Now.Subtract(_mostFocusedUnits_timer).TotalSeconds > 3)
                {
                    RefreshMostFocusedUnits();
                    _mostFocusedUnits_timer = DateTime.Now;
                }
                return _mostFocusedUnits;
            }
        }

        public FocusedUnit MostFocusedUnit
        {
            get
            {
                return MostFocusedUnits.FirstOrDefault();
            }
        }

        public List<WoWUnit> AddsOnMe
        {
            get
            {
                return ObjectManager.GetObjectsOfType<WoWUnit>(false, false).
                    Where(x => x.CurrentTargetGuid == Me.Guid && IsAttackable(x)).
                    OrderBy(x => x.Distance2DSqr).ToList();
            }
        }
    }
}
